package main

/**
反射：
	可以在程序运行时，动态获取的信息。典型的是动态获取某个的类的成员属性、方法。
	即便我们不知道 Dog 类的属性类型、成员方法细节时，依然可以通过反射来动态获取和调用，非常灵活
反射的缺点：
	1.解析工作都是在运行时而非编译期间进行，所以势必对程序性能带来负面影响
	2.反射代码的可读性和可维护性比起正常调用差很多
	3.反射代码出错不能在构建时被捕获，而是在运行时以恐慌的形式报告，这意味着反射错误有可能使你的程序崩溃。
**/

import (
	"fmt"
	"reflect"
)

type Animal struct {
	Name string
}

type Pet struct {
	Name string
}

//Animal独有的方法
func (a Animal) Call() string {
	return "动物的叫声..."
}

//Dog 独有的方法
func (this Dog) Guard() string {
	return "汪汪可以看家"
}

//Pet 独有的方法
func (this Pet) Lu() string {
	return "宠物可以撸"
}

//Animal和Pet，都有这个同名方法
func (a Animal) GetName() string {
	return a.Name
}

//Animal和Pet，都有这个同名方法
func (p Pet) GetName() string {
	return p.Name
}

//可以理解为 Dog这个类，同时继承了Animal和Pet
type Dog struct {
	Animal
	Pet
}

func main() {
	animal := Animal{"中华田园犬"}
	pet := Pet{"宠物狗"}
	dog := Dog{animal, pet}
	dogType := reflect.TypeOf(dog)
	fmt.Println("类型是:", dogType)

	dogValue := reflect.ValueOf(&dog).Elem()
	fmt.Println("================ 通过反射获取属性 ================")
	for i := 0; i < dogValue.NumField(); i++ {
		// 获取属性名
		fmt.Println("属性名:", dogValue.Type().Field(i).Name)
		// 获取属性类型
		fmt.Println("属性类型:", dogValue.Type().Field(i).Type)
		// 获取属性值
		fmt.Println("属性值:", dogValue.Field(i))
		fmt.Println("---------")
	}

	fmt.Println("================ 通过反射获取方法 ================")
	for j := 0; j < dogValue.NumMethod(); j++ {
		// 获取方法名
		fmt.Println("方法名:", dogValue.Type().Method(j).Name)
		// 获取方法类型
		fmt.Println("方法类型:", dogValue.Type().Method(j).Type)
		// 调用该方法
		fmt.Println("执行方法，并输出结果:", dogValue.Method(j).Call([]reflect.Value{}))
		fmt.Println("---------")
	}
}
